package com.bonc.utils;

import java.io.IOException;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.apache.commons.lang.StringUtils;
import org.codehaus.jackson.JsonParser;
import org.codehaus.jackson.JsonParseException;
import org.codehaus.jackson.annotate.JsonIgnoreProperties;
import org.codehaus.jackson.map.DeserializationConfig;
import org.codehaus.jackson.map.JsonMappingException;
import org.codehaus.jackson.map.ObjectMapper;




/*
 * 通用的JSON输入参数实体，不需要为每个请求定义特定的请求实体
 * 
 * 日期转换默认"yyyy-MM-dd"，如果要使用"yyyy-MM-dd hh:mm:ss"或其他格式，显示调用setDataFormat
 */
@JsonIgnoreProperties(ignoreUnknown=true)
public class JsonBody extends HashMap<String, Object> implements Map<String, Object> {
	private static final long serialVersionUID = 1L;
	
	private String dateFormat;
	
	/*
	 * 默认构造函数
	 */
	public JsonBody() {
		
	}

	/*
	 * 通过Map<String, Object>对象构造函数
	 * 增加此函数主要是不能直接用在@RequestParameter从FORM中取值，如以后改进可不用此方法
	 */
	public JsonBody(Map<String, Object> map) {
		if(map != null)
			this.putAll(map);
	}

	/*
	 * 获取key指定的值，key的格式为"param.unit1.key1"
	 * 返回值：HashMap<String, Object>
	 */
	@SuppressWarnings("unchecked")
	public Object getValue(String key) {
		Object temp = this;
		if(key == null || "".equals(key)) {
			return temp;
		}
		String [] keys = key.split("\\.");
		for(int i = 0; i < keys.length; i++) {
			if(keys[i].isEmpty() || temp == null) {
				return null;
			} else {
				//temp's type must be Map(using temp.get)
				if(!(temp instanceof Map)) {
					System.out.println("Error: JsonBody.getValue: " + "object" + "'s type is not Map");
					return null;
				}
				if(keys[i].endsWith("]")) {
					String [] strs = keys[i].split("\\[");
					if(strs.length != 2) {
						System.out.println("Error: JsonBody.getValue: " + keys[i] + " is invalid for List");
						return null;
					}
					String name = strs[0];
					String index = strs[1].substring(0, strs[1].length() - 1);
					Object object = ((HashMap<String, Object>)temp).get(name);
					if(!(object instanceof List)) {
						System.out.println("Error: JsonBody.getValue: " + name + "'s type is not List.");
						return null;
					}
					temp = ((List<?>)object).get(Integer.valueOf(index));
				} else {
					temp = ((HashMap<String, Object>)temp).get(keys[i]);
				}
				//if temp's type is List report to Error
				if(temp instanceof List) {
					System.out.println("Error: JsonBody.getValue: " + keys[i] + "'s type is List");
					return null;
				}
			}
		}
		return temp;
	}
	
	/*
	 * 设置key指定的值，key的格式为"param.unit1.key1"
	 * 没有返回值
	 */
	public void setValue(String key, Object obj){
		this.put(key, obj);
		return;
	}
	
	/*
	 * 将key指定的值转换为JSON字符串
	 * 返回值：标准的JSON字符串
	 */
	public String toJson(String key) {
		Object obj = getValue(key);
		if(obj == null)
			return null;
		ObjectMapper objectMapper = new ObjectMapper();
		//objectMapper.configure(JsonParser.Feature.ALLOW_UNQUOTED_FIELD_NAMES, true);
		//objectMapper.configure(JsonParser.Feature.ALLOW_SINGLE_QUOTES, true);
		try {
			return objectMapper.writeValueAsString(obj);
		} catch (JsonParseException e) {
			e.printStackTrace();
		} catch (JsonMappingException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
		
		return null;
	}
	
	/*
	 * 将key指定的值转换为JAVA对象，cls为Object.class
	 * 返回值：外部指定的Object类型
	 */
	public Object toJava(String key, Class<? extends Object> cls) {
		return toJava(key, cls, false);
	}
	public Object toJava(String key, Class<? extends Object> cls, boolean needTrans) {
		Object obj = getValue(key);
		if(obj == null)
			return null;
		if(needTrans) {
			Map map = new HashMap<String, Object>();
			for(String k : (Set<String>)((Map)obj).keySet()){
				Object v = ((Map)obj).get(k);
				map.put(columntoproperty(k), v);
			}
			obj = map;
		}
		ObjectMapper objectMapper = new ObjectMapper();
		//objectMapper.configure(JsonParser.Feature.ALLOW_UNQUOTED_FIELD_NAMES, true);
		//objectMapper.configure(JsonParser.Feature.ALLOW_SINGLE_QUOTES, true);
		objectMapper.configure(DeserializationConfig.Feature.FAIL_ON_UNKNOWN_PROPERTIES, false);
		if(StringUtils.isBlank(this.dateFormat)) {
			this.setDataFormat("yyyy-MM-dd HH:mm:ss"); //如果使用默认的保存后会默认为当日8:00:00，先变通处理
		}
		if(!StringUtils.isBlank(this.dateFormat)) {
			java.text.DateFormat myFormat = new java.text.SimpleDateFormat(dateFormat);
			DeserializationConfig cfg = objectMapper.getDeserializationConfig();
			cfg.setDateFormat(myFormat);
			objectMapper = objectMapper.setDeserializationConfig(cfg);
		}
		try {
			if(obj instanceof String)
				return objectMapper.readValue((String)obj, cls);
			
			return objectMapper.readValue(objectMapper.writeValueAsString(obj), cls);
		} catch (JsonParseException e) {
			e.printStackTrace();
		} catch (JsonMappingException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
		
		return null;
	}
	
	/*
	 * 根据指定的格式判断参数是否合格
	 * 1. 判断指定的字段是否存在；
	 * 2. 判断字段的格式是否合格：长度、大小、等；
	 */
	public boolean isValid(String format) {
		return this.isValid(format, null);
	}
	public boolean isInvalid(String format) {
		return !isValid(format);
	}
	
	public String getDateFormat() {
		return this.dateFormat;
	}
	
	public void setDataFormat(String dataFormat) {
		this.dateFormat = dataFormat;
	}
	
	public int getCode() {
		return (Integer)(this.get("code") == null ? -1 : this.get("code"));
	}
	
	public void setCode(int code) {
		this.put("code", code);
	}
	
	public String getMessage() {
		return (String)(this.get("message") == null ? "" : this.get("message"));
	}
	
	public void setMessage(String message) {
		this.put("message", message);
	}
	
	private Object recursive_process(String namespace, Object obj) {
		
		if(obj == null){
			return "";
		}
		
		if(obj instanceof HashMap){
			HashMap map = (HashMap)obj;
			for(String key : (Set<String>)map.keySet()){
				Object value = map.get(key);
				String newkey = ("".equals(namespace) ? namespace : namespace + ".") + key;
				if(recursive_process(newkey, value) == null){
					return null;
				}
			}
		} else if(obj instanceof String){
			String value = (String)obj;
			System.out.println("recursive_process: " + namespace + " = " + this.getValue(namespace));
			return this.getValue(namespace);
		} else {
			System.out.println("recursive_process: obj is unknown");
			return null;
		}
		
		return obj;
	}
	
	private String columntoproperty(String src) {
		if(src == null) {
			System.out.println("columntoproperty src is null");
			return null;
		}
		StringBuffer sb = new StringBuffer();
		for(String i : src.toLowerCase().split("_")) {
			sb.append(i.substring(0,1).toUpperCase() + i.substring(1, i.length()));
		}
		return sb.substring(0,1).toLowerCase() + sb.substring(1, sb.length());
	}
	
	public boolean isValid(String format, String alias) {
		if(format == null || "".equals(format))
			return true;
		
		ObjectMapper mapper = new ObjectMapper();
		mapper.configure(JsonParser.Feature.ALLOW_SINGLE_QUOTES, true);
		mapper.configure(JsonParser.Feature.ALLOW_UNQUOTED_FIELD_NAMES, true);
		try {
			
			@SuppressWarnings("unchecked")
			Map<String, Object> map = mapper.readValue(format, Map.class);
			@SuppressWarnings("unchecked")
			Map<String, String> mapped = (alias == null ? null : mapper.readValue(alias, Map.class));
			if(recursive_process("", map, mapped) == null)
				return false;
		} catch (Exception e) {
			e.printStackTrace();
			System.out.println(e.getMessage());
			this.put("_invalidMessage", e.getMessage());
			return false;
		}
		
		return true;
	}
	
	private Object recursive_process(String namespace, Object obj, Map<String, String> mapped) throws Exception {
		
		if(obj == null){
			return "";
		}
		
		if(obj instanceof HashMap){
			HashMap map = (HashMap)obj;
			for(String key : (Set<String>)map.keySet()){
				Object value = map.get(key);
				String newkey = ("".equals(namespace) ? namespace : namespace + ".") + key;
				if(recursive_process(newkey, value, mapped) == null){
					return null;
				}
			}
		} else if(obj instanceof String){
			String value = (String)obj;
			//System.out.println("recursive_process: String: " + namespace + " = " + this.getValue(namespace));
			try {
				if(!propertyValueIsValid(value, this.getValue(namespace), (mapped == null ? 0 : 1))) {
					return null;
				}
			} catch (Exception e) {
				if(mapped == null) {
					throw new Exception(namespace + " " + e.getMessage());
				} else {
					String name = mapped.get(namespace);
					name = (name == null ? namespace : name);
					throw new Exception("“" + name + "”" + e.getMessage());
				}
				//return null;
			}
			return Boolean.valueOf(true);
		} else if(obj instanceof Boolean){
			Boolean value = (Boolean)obj;
			//System.out.println("recursive_process: Boolean: " + namespace + " = " + this.getValue(namespace));
			if(value && this.objectIsEmpty(this.getValue(namespace))) {
				throw new Exception(namespace + " is empty");
				//return null;
			}
			return Boolean.valueOf(true);
		} else {
			System.out.println("recursive_process: obj is unknown" + obj.getClass().getName());
			throw new Exception("recursive_process: obj is unknown" + obj.getClass().getName());
			//return null;
		}
		
		return obj;
	}
	
	private boolean propertyValueIsValid(String rule, Object data, int enOrCh) throws Exception {
		if(StringUtils.isBlank(rule)) {
			return true;
		}
		
		String propertyEnum = "required,type,length,max,min,regex,format";
		Map<String, String> map = new HashMap<String, String>();
		String[] rules = rule.split(",");
		for(String i : rules) {
			if(!i.contains(":") || i.split(":").length < 2) {
				System.out.println("parameter's check rule is error! [" + i + "]");
				throw new Exception("check rule is error! [" + i + "]");
				//return false;
			}
			String property = i.split(":")[0].trim().toLowerCase();
			String value = i.substring(i.indexOf(":") + 1);
			if(!property.equals("format") && !property.equals("regex")) {
				value = value.toLowerCase();
			}
			
			if(propertyEnum.indexOf(property) < 0) {
				System.out.println("parameter's check rule is error! property enum in(" + propertyEnum + ")");
				throw new Exception("check rule is error! property enum in(" + propertyEnum + ")");
				//return false;
			}
			
			map.put(property, value);
		}
		
		if(map.get("required") == null) {
			map.put("required", "true");
		}
		
		String required = map.get("required");
		if(required.equals("true") && this.objectIsEmpty(data)) {
			throw new Exception(D.is_empty[enOrCh]);
		} else if(this.objectIsEmpty(data)) {
			return true;
		}
		String type = map.get("type");
		if(type != null) {
			String _invalidMessage = "is invalid";
			String regex = "";
			if(type.equals("string")) {
				regex = "";
			} else if(type.equals("number")) {
				regex = "^[0-9]+$";
				_invalidMessage = D.must_be_number[enOrCh];
			} else if(type.equals("boolean")) {
				regex = "^(true)|(false)$";
				_invalidMessage = D.must_be_true_or_false[enOrCh];
			} else if(type.equals("mobile")) {
				regex = "^1\\d{10}$";
				_invalidMessage = D.must_be_telephone[enOrCh];
			} else if(type.equals("email")) {
				regex = "^[a-z0-9]+([._-]*[a-z0-9])*@([a-z0-9]+[-a-z0-9]*[a-z0-9]+.){1,63}[a-z0-9]+$";
				_invalidMessage = D.must_be_email[enOrCh];
			} else if(type.equals("identity")) {
				regex = "(^\\d{15}$)|(^\\d{17}([0-9xX])$)";
				_invalidMessage = D.must_be_identity[enOrCh];
			} else if(type.equals("date")) {
				regex = "";
				_invalidMessage = D.must_be_date[enOrCh];
			} else {
				throw new Exception(D.is_undefined_type[enOrCh]);
				//return false;
			}
			if(!StringUtils.isBlank(regex)) {
				Pattern pattern = Pattern.compile(regex, Pattern.MULTILINE);
				Matcher matcher = pattern.matcher(data.toString());
				if(!matcher.find()) {
					throw new Exception(_invalidMessage);
					//return false;
				}
			}
		}
		
		String length = map.get("length");
		if(length != null) {
			String maxLength = "0";
			String minLength = "0";
			String[] lengths = length.split("-");
			if(lengths.length == 1 && StringUtils.isNumeric(lengths[0])) {
				maxLength = lengths[0];
			} else if(lengths.length == 2 && StringUtils.isNumeric(lengths[0]) && StringUtils.isNumeric(lengths[1])){
				minLength = lengths[0];
				maxLength = lengths[1];
			} else {
				System.out.println("parameter's check rule is error! length must be [maxLength] or [minLength-maxLength]");
				throw new Exception("check rule is error! length must be [maxLength] or [minLength-maxLength]");
				//return false;
			}
			
			if(((String)data).length() < Integer.valueOf(minLength)|| ((String)data).length() > Integer.valueOf(maxLength)) {
				throw new Exception(D.is_invalid_length[enOrCh]);
			}
		}
		
		String min = map.get("min");
		if(min != null) {
			if(!StringUtils.isNumeric(min)) {
				System.out.println("min is not numeric");
				throw new Exception("check rule is error! min is not numeric");
				//return false;
			}
			if(!StringUtils.isNumeric(data.toString())) {
				throw new Exception(D.is_not_numeric[enOrCh]);
			}
			if(Double.valueOf(data.toString()) < Double.valueOf(min)) {
				throw new Exception(D.is_less_than_min[enOrCh]);
			}
		}
		
		String max = map.get("max");
		if(max != null) {
			if(!StringUtils.isNumeric(max)) {
				System.out.println("max is not numeric");
				throw new Exception("check rule is error! max is not numeric");
				//return false;
			}
			if(!StringUtils.isNumeric(data.toString())) {
				throw new Exception(D.is_not_numeric[enOrCh]);
			}
			if(Double.valueOf(data.toString()) > Double.valueOf(max)) {
				throw new Exception(D.is_more_than_max[enOrCh]);
			}
		}
		
		String regex = map.get("regex");
		if(regex != null) {
			Pattern pattern = Pattern.compile(regex, Pattern.MULTILINE);
			Matcher matcher = pattern.matcher(data.toString());
			if(!matcher.find()) {
				throw new Exception(D.regex_is_invalid[enOrCh]);
			}
		}
		
		String format = map.get("format");
		if(format != null && type.equals("date")) {
			SimpleDateFormat sdf = new SimpleDateFormat(format);
			try {
				Date date = sdf.parse(data.toString());
				String newStr = new SimpleDateFormat(format).format(date);
				if(!newStr.equals(data)) {
					throw new Exception(D.may_be_unexpected[enOrCh] + ": " + newStr);
				}
			} catch (ParseException e) {
				throw new Exception(D.is_invalid_date[enOrCh]);
			}
		}
		
		return true;
	}
	
	private boolean objectIsEmpty(Object value) {
		if(value == null) {
			return true;
		}
		if(value instanceof String) {
			return StringUtils.isBlank((String)value);
		}
		return false;
	}
	
	public String getInvalidMessage() {
		return this.get("_invalidMessage").toString();
	}
	
	public interface D {
		public int en = 0;
		public int ch = 1;
		public String[] is_undefined_type       = {"is_undefined_type", "未定义的类型"};
		public String[] is_empty                = {"is empty", "为空"};
		public String[] must_be_number          = {"must be number", "必须为数字"};
		public String[] must_be_true_or_false   = {"must be true or false", "必须为布尔值：true或false"};
		public String[] must_be_telephone       = {"must be telephone", "必须为正确的手机号码"};
		public String[] must_be_email           = {"must be email", "必须为正确的电子邮件地址"};
		public String[] must_be_identity        = {"must be identity", "必须为正确的身份证号码"};
		public String[] must_be_date            = {"must be date", "必须为format指定格式的日期"};
		public String[] is_invalid_length       = {"is invalid length", "长度不正确"};
		public String[] is_not_numeric          = {"is not numeric", "必须为数字"};
		public String[] is_less_than_min        = {"is less than min", "小于允许的最小值"};
		public String[] is_more_than_max        = {"is more than max", "大于允许的最大值"};
		public String[] regex_is_invalid        = {"regex is invalid", "不满足正则表达式规则"};
		public String[] may_be_unexpected       = {"may be unexpected", "不是预期的日期"};
		public String[] is_invalid_date         = {"is invalid date with format", "不是format指定格式的日期"};
	}
}
